home *** CD-ROM | disk | FTP | other *** search
/ PC Media 22 / PC MEDIA CD22.iso / share / prog / datalib2 / record.cpp < prev    next >
C/C++ Source or Header  |  1995-08-14  |  15KB  |  674 lines

  1. #include "datapriv.hpp"
  2.  
  3. int recstrcmp(void *,void *);
  4.  
  5. /********************* Accessors ********************************/
  6.  
  7. char *record::indname()
  8. {
  9.  if (oi) return oi->name;
  10.  return 0;
  11. }
  12.  
  13.  
  14. /********************* RECORD FUNCTIONS *************************/
  15.  
  16. // This is the constructor for a record, which grabs the space
  17. // for it
  18.  
  19. /********************* CONSTRUCTOR *****************************/
  20.  
  21. record::record(database &dbp,char *indname)
  22. {
  23.  if (!(recbuf=new char[dbp.reclen+1]) || 
  24.      !(recbufo=new char[dbp.reclen+1]) ||
  25.      !(fieldwork=new char[dbp.maxfieldl+1]))
  26.     dbfer(NORECSP);
  27.  
  28.  if (dbp.isvalid()) {dbfer(INVDB); return;}
  29.  
  30.  memset(recbuf,' ',dbp.reclen+1);     // Clear record
  31.  memset(recbufo,' ',dbp.reclen+1);
  32.  rbp=recbuf;
  33.  
  34.  db=&dbp;
  35.  rn=-1;            // Indicate no record has been read into recbuf
  36.  delstate=NOTDEL;
  37.  
  38.  ltype=-1;        // Flag no last key found
  39.  curind=0;
  40.  nextind=0;
  41.  
  42.  next=dbp.firstrec;    // Link into record tree
  43.  dbp.firstrec=this;
  44.  prev=0;
  45.  
  46.  // Now check for index, if found then copy in top cluster
  47.  
  48.  oi=0;
  49.  
  50.  if (oi=dbp.findex)
  51.  {
  52.   if (indname) oi=dbp.getindex(indname);
  53.  
  54.   if (oi)
  55.   {
  56.    nextind=oi->firstrec;        // Link into index tree
  57.    oi->firstrec=this;
  58.    curind=new indpt(oi->topind);
  59.   }
  60.  }
  61. }
  62.  
  63. /********************** DESTRUCTOR *****************************/
  64.  
  65. // This is the destructor for a record, which also cleans up the pointers
  66.  
  67. record::~record(void)
  68. {
  69.  if (oi)
  70.  {
  71.   record *rp,*fp;
  72.   fp=rp=oi->firstrec;
  73.   while(rp)
  74.   {
  75.    if (rp==this)
  76.    {
  77.     if (fp==rp) oi->firstrec=nextind;
  78.     else {while(fp->nextind!=rp) fp=fp->nextind; fp->nextind=nextind;}
  79.     break;
  80.    }
  81.    rp=rp->nextind;
  82.   }
  83.  }
  84.  killind();
  85.  if (recbuf) delete recbuf;
  86.  if (recbufo) delete recbufo; 
  87.  if (fieldwork) delete fieldwork;
  88.  if (prev) prev->next=next; else db->firstrec=next;
  89.  if (next) next->prev=prev;
  90. }
  91.  
  92. // Kill all indclus parents of the record
  93.  
  94. void record::killind(void)
  95. {
  96.  indpt *pt,*npt;
  97.  
  98.  if (curind)        // Delete all references to current index
  99.  {
  100.   pt=curind;
  101.   do
  102.   {
  103.    npt=pt->parent; delete pt; pt=npt;
  104.   }
  105.   while (pt);
  106.  }
  107. }
  108.  
  109. /********************* Record Assignment ***************/
  110.  
  111. void record::operator=(record &s)
  112. {
  113.  indpt *pt,*npt;
  114.  
  115.  if (curind)
  116.  {
  117.   killind();
  118.   curind=new indpt(s.curind->icp); curind->currec=s.curind->currec;
  119.  
  120.   pt=(s.curind)->parent;
  121.   npt=curind;
  122.   while (pt)
  123.   {
  124.    npt->parent=new indpt(pt->icp); npt->currec=pt->currec;
  125.    npt=npt->parent; pt=pt->parent;
  126.   }
  127.  }
  128.  delstate=s.delstate;
  129.  rn=s.rn;
  130.  rbp=recbuf;
  131.  indflg=s.indflg;
  132.  oi=s.oi;
  133.  memmove(lkey,s.lkey,128); ltype=s.ltype; lseltype=s.lseltype;
  134.  memmove(recbuf,s.recbuf,db->reclen+1);
  135.  memmove(recbufo,s.recbuf,db->reclen+1);
  136.  memmove(fieldwork,s.fieldwork,db->maxfieldl+1);
  137. }
  138.  
  139. /********************* SET the delete state ********************/
  140.  
  141. void record::setdelstate(int sval)
  142. {
  143.  if (sval!=DEL && sval!=NOTDEL) return;
  144.  
  145.  delstate=sval;
  146.  *recbuf=(sval==DEL) ? '*' : ' ';
  147. }
  148.  
  149. /********************* SELECT record n from dbf ****************/
  150.  
  151. int record::seldbf(long n)
  152. {
  153.  return select(n,ALL,1);
  154. }
  155.  
  156. /********************* SELECT a record *************************/
  157.  
  158. // Select a record from the data file
  159.  
  160. int record::select(long n,int type,int dbfrec)
  161. {
  162.  long ln=n;    // Next Record
  163.  long fp;
  164.  
  165.  if (n>0)    // Select by record number
  166.  {
  167.   int rv;
  168.  
  169.   if (type==ALL && (!curind || dbfrec))    // Fast select direct from dbf
  170.   {
  171.    if (n<1 || n>db->nrec) return CANTSEL;
  172.    rn=n;
  173.    fp=db->recstart+db->reclen*(rn-1); Fseek(db->dbfp,fp,SEEK_SET);
  174.    Fread(recbuf,db->reclen,1,db->dbfp);
  175.    memmove(recbufo,recbuf,db->reclen+1);
  176.   }
  177.   else
  178.   {
  179.    rv=select(FIRST,type); if (rv) return rv;
  180.    for(long i=1; i<n; i++) {rv=select(NEXT,type); if (rv) return rv;}
  181.   }
  182.  }
  183.  else        // Select FIRST, LAST, NEXT or PREVIOUS
  184.   do
  185.   {
  186.    if (curind)
  187.    {
  188.     long tn;
  189.    
  190.     if (!(tn=getind(ln))) return CANTSEL;
  191.     rn=tn;
  192.    }
  193.    else 
  194.    {
  195.     if ((rn<=1 && ln==PREVIOUS) || (rn>=db->nrec && ln==NEXT)) return CANTSEL;
  196.     switch(ln)
  197.     {
  198.      case FIRST     : rn=1; break;
  199.      case LAST     : rn=db->nrec; break;
  200.      case NEXT     : rn++; break;
  201.      case PREVIOUS : rn--; break;
  202.     }
  203.    }
  204.  
  205.    fp=db->recstart+db->reclen*(rn-1);
  206.    Fseek(db->dbfp,fp,SEEK_SET);
  207.    Fread(recbuf,db->reclen,1,db->dbfp);     // Read the record
  208.    memmove(recbufo,recbuf,db->reclen+1);
  209.    if (n==FIRST) ln=NEXT;         // If deleted, which record next
  210.    if (n==LAST) ln=PREVIOUS;
  211.   }
  212.   while((type==DEL && *rbp==' ') || (type==NOTDEL && *rbp=='*'));
  213.  
  214.  delstate=(*rbp=='*') ? DEL : NOTDEL;
  215.  
  216.  return 0;
  217. }
  218.  
  219. /********************* RECORD::SELECT - By Values ************/
  220.  
  221. // This is for finding records where the expression is true
  222.  
  223. int record::select(char *expr,long n,int type)
  224. {
  225.  long ln=n;
  226.  int i;
  227.  int rv,rt;
  228.  char ws[128];
  229.  
  230.  if (n>0)
  231.  {
  232.   rv=select(expr,FIRST,type); if (rv) return rv;
  233.   for(i=1; i<n; i++)
  234.   {
  235.    if (rv=select("",NEXT,type)) return rv;
  236.   }
  237.   return(0);
  238.  }
  239.  
  240.  if (rv=select(n,type)) return rv;    // Position at first possible record
  241.  if (n==FIRST) ln=NEXT;
  242.  if (n==LAST) ln=PREVIOUS;
  243.  if (*expr) eval(expr,ws,rv);        // Tokenise expression if not already
  244.  
  245.  while(1)
  246.  {
  247.   rv=eval(ws,rt);
  248.   if (!rv || rt!=OPLOG) return CANTSEL;
  249.   if (*ws=='T') return(0);
  250.   if (rv=select(ln,type)) return(rv);
  251.  }
  252. }
  253.  
  254. // This is for numbers, call with record::compnum
  255. // which compares two numbers
  256.  
  257. int record::select(int fieldnum,int value,long n,int type)
  258. {
  259.  return(select(fieldnum,&value,compnum,n,type));
  260. }
  261.  
  262. int compnum(void *field,void *num)
  263. {
  264.  return(atoi((char *)field)!=*(int *)num);
  265. }
  266.  
  267. // First function is string, simply call user defined select with
  268. // the strcmp function
  269.  
  270. int record::select(int fieldnum,char *value,long n,int type)
  271. {
  272.  return(select(fieldnum,value,recstrcmp,n,type));
  273. }
  274.  
  275. int recstrcmp(void *s1,void *s2)
  276. {
  277.  return(strcmp(trim((char *)s1),trim((char *)s2)));
  278. }
  279.  
  280. // This is the user defined select function which allows the
  281. // user to define a function to compare a field with a value,
  282. // and then selects those record for which the function returns
  283. // 0
  284.  
  285. int record::select(int fieldnum,void *value,
  286.                    int (*cmp)(void *,void *),long n,int type)
  287. {
  288.  long ln=n;
  289.  field *fp;
  290.  int i;
  291.  int rv;
  292.  
  293.  if (n>0)
  294.  {
  295.   select(fieldnum,value,cmp,FIRST,type);
  296.   for(i=1; i<n; i++)
  297.   {
  298.    if (rv=select(fieldnum,value,cmp,NEXT,type)) return rv;
  299.   }
  300.   return(0);
  301.  }
  302.  
  303.  if (!(fp=db->getfield(fieldnum))) return CANTSEL;
  304.  
  305.  if (rv=select(n,type)) return rv;    // Position at first possible record
  306.  if (n==FIRST) ln=NEXT;
  307.  if (n==LAST) ln=PREVIOUS;
  308.  
  309.  while(1)
  310.  {
  311.   strncpy(fieldwork,rbp+fp->recpos,fp->len);
  312.   *(fieldwork+fp->len)=0;
  313.   if (!(*cmp)(fieldwork,value)) return(0);
  314.   if (rv=select(ln,type)) return(rv);
  315.  }
  316. }
  317.  
  318. /********************* Field Operations on the record  *********/
  319.  
  320. // This is overloaded, the 1st function returns by field number
  321.  
  322. // Get field by field number
  323.  
  324. char *record::getfield(int n,int trimflag)
  325. {
  326.  field *fld;
  327.  
  328.  if (n<1 || n>db->nfield || rn<0)
  329.  {
  330.   memset(fieldwork,' ',db->maxfieldl); fieldwork[db->maxfieldl]=0;
  331.  }
  332.  else
  333.  {
  334.   fld=db->getfield(n);
  335.   strncpy(fieldwork,rbp+fld->recpos,fld->len);
  336.   *(fieldwork+fld->len)=0;
  337.   if (fld->gettype()=='M')    // Memo Field...
  338.   {
  339.    if (*fieldwork=='~')        // New memo written
  340.    {
  341.     char *rv=*(char **)(rbp+fld->recpos+1);
  342.     return rv;
  343.    }
  344.    long bn=atol(fieldwork);
  345.    char *memow=db->memow;    // Local copies of db variables
  346.    int memowl=db->memowl;
  347.    FILE *dbtp=db->dbtp;
  348.  
  349.    if (bn)
  350.    {
  351.     char *ep;                // end of memo
  352.     char *cbp=memow;            // Current block pointer
  353.     int ef=0;                // Flag end of memo found
  354.  
  355.     if (!memow) memow=new char[512];
  356.  
  357.     Fseek(dbtp,bn*512L,SEEK_SET);
  358.     for(int i=0; i<memowl; i++)        // Read blocks until end found
  359.     {
  360.      Fread(cbp,512,1,dbtp);
  361.      if (ep=strstr(memow,"\x1a\x1a")) {*ep=0; ef=1; break;}
  362.      cbp+=512;
  363.     }
  364.     if (feof(dbtp) && !ef) {dbfer(INVMEMO); return ("");}  // Invalid memo
  365.     if (!ef)                 // Need to find new memo length
  366.     {
  367.      memowl=0;
  368.      Fseek(dbtp,bn*512L,SEEK_SET);
  369.      do
  370.      {
  371.       memowl++;
  372.       Fread(memow,1,512,dbtp);
  373.       if (ep=strstr(memow,"\x1a\x1a")) {*ep=0; ef=1;}
  374.      }
  375.      while(!ef && !feof(dbtp));
  376.      if (memowl>1) {free(memow); memow=new char[memowl*512];}
  377.      if (!memow) {dbfer(NOMEMSP); return("");}
  378.      db->memow=memow;
  379.      db->memowl=memowl;
  380.      return(getfield(n,trimflag));
  381.     }
  382.    }
  383.    else {*fieldwork=0; return fieldwork;}
  384.  
  385.    if (trimflag) return(rtrim(memow)); return(memow);
  386.   }
  387.  }
  388.  
  389.  if (trimflag) return(rtrim(fieldwork));
  390.  return(fieldwork);
  391. }
  392.  
  393. // Get field by field name
  394.  
  395. char *record::getfield(char *name,int trimflag)
  396. {
  397.  field *fld;
  398.  long grn=0;
  399.  
  400.  if (fld=db->getfield(name)) grn=fld->getnumber();
  401.  
  402.  return(getfield(grn,trimflag));
  403. }
  404.  
  405. /******************** Set Field Operations *********************/
  406.  
  407. int record::doset(char *value,field *fld)
  408. {
  409.  char *sp=value;
  410.  char *fp=rbp+fld->recpos;
  411.  
  412.  for(int i=0; i<fld->len; i++) *fp++=(*sp) ? *sp++ : ' ';
  413.  return 0;
  414. }
  415.  
  416. int record::setfield(int num,char *value)
  417. {
  418.  field *fld;
  419.  int i,obp;
  420.  char *fp;
  421.  
  422.  if (!(fld=db->getfield(num))) return RECNOTSET;
  423.  switch(fld->gettype())
  424.  {
  425.   case 'C' : return doset(value,fld);
  426.  
  427.   case 'D' : if (strlen(value)!=8) return RECNOTSET;
  428.          for(i=0; i<strlen(value); i++)
  429.           if (!isdigit(value[i])) return RECNOTSET;
  430.          return doset(value,fld);
  431.  
  432.   case 'L' : strupr(value);
  433.          if (*value=='T' || *value=='F' ||
  434.          *value=='Y' || *value=='N') return doset(value,fld);
  435.  
  436.   case 'M' : fp=rbp+fld->recpos; *fp='~'; *(char **)(fp+1)=value; return 0;
  437.  
  438.   default  : return RECNOTSET;
  439.  }
  440. }
  441.  
  442. int record::setfield(int num,double value)
  443. {
  444.  field *fld=db->getfield(num);
  445.  
  446.  if (!fld || fld->gettype()!='N') return RECNOTSET;
  447.  
  448.  char ws[128],ss[128];
  449.  int i,nex,wslen;
  450.  
  451.  sprintf(ss,"%%.%df",fld->getrdp());    // May need %f here ?
  452.  wslen=sprintf(ws,ss,value);
  453.  nex=fld->getlen()-wslen;
  454.  if (nex>0) {memmove(ws+nex,ws,wslen+1); for(i=0; i<nex; i++) ws[i]=' ';}
  455.  return doset(ws,fld);
  456. }
  457.  
  458. int record::setfield(char *name,double value)
  459. {
  460.  field *fld=db->getfield(name);
  461.  
  462.  if (!fld) return RECNOTSET;
  463.  
  464.  return setfield(fld->getnumber(),value);
  465. }
  466.  
  467. int record::setfield(char *name,char *value)
  468. {
  469.  field *fld=db->getfield(name);
  470.  
  471.  if (!fld) return RECNOTSET;
  472.  
  473.  return setfield(fld->getnumber(),value);
  474. }
  475.  
  476. /******************** Write Record Operations ******************/
  477.  
  478. int record::write(int type)
  479. {
  480.  index *ip;
  481.  char res[128],reso[128];    // index results
  482.  int dum;
  483.  int nb;            // No. of bytes written to file
  484.  long fp;            // Position in file to write record
  485.  record *upp;            // Pointer to records using index
  486.  long urn;            // record no. of  "    "     "
  487.  int e=1;            // error number
  488.  int ret;            // Return value
  489.  
  490.  if (rn<1 && type==OVER) return NOWRUNR;
  491.  
  492.  if (db->dbtp) wmemo(type);    // Check if any memos need writing
  493.  
  494.  if (type==OVER)    // Overwrite, set position in dbf file
  495.  {
  496.   fp=db->recstart+db->reclen*(rn-1);
  497.   Fseek(db->dbfp,fp,SEEK_SET);
  498.   nb=Fwrite(recbuf,1,db->reclen,db->dbfp);
  499.  
  500.   ip=db->findex;
  501.   record *dbr=db->firstrec;
  502.   while(dbr)
  503.   {
  504.    if (dbr->rn==rn && dbr!=this)
  505.    {
  506.     memmove(dbr->recbufo,recbuf,db->reclen+1);
  507.     memmove(dbr->recbuf,recbuf,db->reclen+1);
  508.     dbr->delstate=delstate;
  509.    }
  510.    dbr=dbr->next;
  511.   }
  512.   while(ip)
  513.   {
  514.    memmove(db->exwork,ip->tindexp,512);
  515.    rbp=recbufo;
  516.    eval(reso,dum);
  517.    rbp=recbuf;
  518.    eval(res,dum);
  519.    if (memcmp(res,reso,ip->topind->explen))
  520.    {
  521.     delkey(reso,ip);
  522.     inskey(res,ip);
  523.  
  524.     upp=ip->firstrec;        // Update record keys
  525.     while(upp)
  526.     {
  527.      if (upp->rn>=0)
  528.      {
  529.       urn=upp->rn;
  530.       upp->eval(res,dum);
  531.       if (upp->selkey(res,READIND,ip->indtype)) goto error;
  532.       while(upp->rn!=urn) if (upp->selkey()) {e=2; goto error;}
  533.      }
  534.      upp=upp->nextind;
  535.     }
  536.    }
  537.    ip=ip->next;
  538.   }
  539.  }
  540.  else            // Add a new record at the file end
  541.  {
  542.   rn=++(db->nrec);
  543.  
  544.   ip=db->findex;
  545.   while(ip)
  546.   {
  547.    memmove(db->exwork,ip->tindexp,512);        // copy over index epression
  548.    eval(res,dum);
  549.    inskey(res,ip);
  550.  
  551.    upp=ip->firstrec;        // Update record keys
  552.    while(upp)
  553.    {
  554.     if (upp->rn>=0)
  555.     {
  556.      urn=upp->rn;
  557.      upp->eval(res,dum);
  558.      if (upp->selkey(res,READIND,ip->indtype)) {e=3; goto error;}
  559.      while(upp->rn!=urn) if (upp->selkey()) {e=4; goto error;}
  560.     }
  561.     upp=upp->nextind;
  562.    }
  563.    ip=ip->next;
  564.   }
  565.  
  566.   fp=db->recstart+db->reclen*(rn-1);
  567.   Fseek(db->dbfp,fp,SEEK_SET);
  568.   nb=Fwrite(recbuf,1,db->reclen,db->dbfp);
  569.  }
  570.  
  571.  memmove(recbufo,recbuf,db->reclen+1);
  572.  ret=(nb==db->reclen) ? 0 : WRFAIL;
  573.  db->change=1;
  574.  
  575.  return ret;
  576.  
  577.  error :    // Fatal error in index write !
  578.  
  579.  dbfer(ERINDW);
  580.  // printf("\nError code %d in index jig, rn %d\n",e,urn);
  581.  return WRFAIL;
  582. }
  583.  
  584. // This routine checks to see if the record has any memos to write
  585.  
  586. void record::wmemo(int type)
  587. {
  588.  char *ws=new char[512];
  589.  
  590.  for(int i=1; i<=db->nfield; i++)
  591.  {
  592.   field *fld=db->getfield(i);
  593.   if (fld->gettype()=='M')        // Memo field
  594.   {
  595.    char *mp=recbuf+fld->recpos;
  596.  
  597.    if (*mp=='~')    // A new memo field has been written
  598.    {
  599.     long bp=0;                  // New buffer pointer
  600.     unsigned int len;              // Length of new memo
  601.     char *memo=*(char **)(mp+1);
  602.  
  603.     if (len=strlen(memo))
  604.     {
  605.      if (len<=510) bp=atol(recbufo+fld->recpos); // Fit in the old block ?
  606.      if (type==NEW || !bp)             // Must create a new block
  607.      {
  608.       Fseek(db->dbtp,0,SEEK_SET); Fread(ws,1,512,db->dbtp);
  609.       bp=*(long *)ws;
  610.       *(long *)ws=bp+len/512+1;
  611.       Fseek(db->dbtp,0,SEEK_SET); Fwrite(ws,1,512,db->dbtp);
  612.      }
  613.      Fseek(db->dbtp,(long)(bp*512L),SEEK_SET);
  614.      Fwrite(memo,1,len,db->dbtp);
  615.      fputc(0x1a,db->dbtp); fputc(0x1a,db->dbtp); len+=2;
  616.      memset(ws,' ',512);
  617.      if (len & 0x1ff) Fwrite(ws,1,(512-(len & 0x1ff)),db->dbtp);
  618.      sprintf(ws,"%010ld",bp);
  619.      memcpy(mp,ws,10);
  620.     }
  621.     else memset(mp,'0',10);
  622.    }
  623.   }
  624.  }
  625.  
  626.  delete ws;
  627. }
  628.  
  629. /****************************************************************
  630.  
  631. EVAL routines
  632.  
  633. *****************************************************************/
  634.  
  635. int record::eval(char *expr,void *result,int &rtype)
  636. {
  637.  char *wsp;
  638.  
  639.  if (!db->ex)
  640.  {
  641.   db->ex=new expval(db);
  642.   db->exwork=new char[512];
  643.  }
  644.  if (!db->ex || !db->exwork || db->ex->erflag==2) return 0;
  645.  db->ex->erflag=0;
  646.  
  647.  wsp=db->exwork;
  648.  db->ex->exetoken(expr,&wsp);
  649.  if (db->ex->erflag==1) {db->ex->erflag=3; return 0;}    // show tokeniser error
  650.  return eval(result,rtype);
  651. }
  652.  
  653. int record::eval(void *result,int &rtype)
  654. {
  655.  op *rv;
  656.  char *wsp;
  657.  
  658.  if (!db->ex || db->ex->erflag>1) return 0;    // Fatal or tokeniser error
  659.  
  660.  wsp=db->exwork;
  661.  indflg=0;
  662.  rv=db->ex->exeval(&wsp,this);
  663.  if (db->ex->erflag) return 0;
  664.  rtype=rv->optype;
  665.  if (rtype==OPINT) *(double *)result=rv->num;
  666.  if (rtype==OPSTR) strcpy((char *)result,rv->str);
  667.  if (rtype==OPDATE) strcpy((char *)result,rv->date);
  668.  if (rtype==OPLOG) 
  669.    {*(char *)result=(rv->num) ? 'T' : 'F'; ((char *)result)[1]=0;}
  670.  return 1;
  671. }
  672.  
  673.  
  674.